// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_d3d9_texture.h"
#include "mk_error.h"
#include "mk_engine.h"
#include "mk_d3d9_render_device.h"
#include "mk_d3d9_utilities.h"
#include "mk_file_system.h"
#include "mk_memory_block.h"

namespace MK3DE
{
	CD3D9Texture2D::CD3D9Texture2D() : _texture(NULL)
	{
		memset(&_imageInfo, 0, sizeof(_imageInfo));
	}

	CD3D9Texture2D::CD3D9Texture2D(const char* fileName) : CTexture2D(fileName), _texture(NULL)
	{
		memset(&_imageInfo, 0, sizeof(_imageInfo));
	}

	CD3D9Texture2D::~CD3D9Texture2D()
	{
		SAFE_RELEASE(_texture);
	}

	void CD3D9Texture2D::__ObtainActualTextureSize()
	{
		ASSERT(_texture);

		D3DSURFACE_DESC surfDesc;
		HRESULT hr = _texture->GetLevelDesc(0, &surfDesc);
		if (SUCCEEDED(hr))
		{
			_width = (DWORD)surfDesc.Width;
			_height = (DWORD)surfDesc.Height;
		}
		else
		{
			_width = (DWORD)_imageInfo.Width;
			_height = (DWORD)_imageInfo.Height;
		}
	}

	BOOL CD3D9Texture2D::Load()
	{
		BOOL ret = FALSE;
		CMemoryBlock block;

		ret = GetEngine()->GetFileSystem()->ReadFile(_fileName.c_str(), &block);
		MK_PROCESS_ERROR(ret);

		CD3D9RenderDevice* rd = (CD3D9RenderDevice*)GetEngine()->GetRenderDevice();
		LPDIRECT3DDEVICE9 d = rd->GetD3DDevice();

		HRESULT hr = D3DXCreateTextureFromFileInMemoryEx(d, 
			(void*)block.GetBuffer(), (UINT)block.GetSize(),
			0, 0, 0, TU_NORMAL, D3DFMT_FROM_FILE, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			&_imageInfo, NULL, &_texture);
		MK_PROCESS_COM_ERROR(hr);

		__ObtainActualTextureSize();

		_mip = 0;
		_usage = TU_NORMAL;
		_format = FormatMapping(_imageInfo.Format);


		ret = TRUE;

Exit0:
		return ret;
	}

	BOOL CD3D9Texture2D::LoadFromBuffer(const BYTE* buffer, DWORD size)
	{
		BOOL ret = FALSE;

		CD3D9RenderDevice* rd = (CD3D9RenderDevice*)GetEngine()->GetRenderDevice();
		LPDIRECT3DDEVICE9 d = rd->GetD3DDevice();

		HRESULT hr = D3DXCreateTextureFromFileInMemoryEx(d, 
			(void*)buffer, (UINT)size,
			0, 0, D3DX_FROM_FILE, TU_NORMAL, D3DFMT_FROM_FILE, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			&_imageInfo, NULL, &_texture);
		MK_PROCESS_COM_ERROR(hr);

		__ObtainActualTextureSize();

		_mip = 0;
		_usage = TU_NORMAL;
		_format = FormatMapping(_imageInfo.Format);


		ret = TRUE;

Exit0:
		return ret;
	}

	BOOL CD3D9Texture2D::Create(DWORD width, DWORD height, PIXEL_FORMAT format, DWORD mip, 
		TEXTURE_USAGE usage)
	{
		CD3D9RenderDevice* rd = (CD3D9RenderDevice*)GetEngine()->GetRenderDevice();
		LPDIRECT3DDEVICE9 d = rd->GetD3DDevice();

		D3DFORMAT d3dformat = FormatMapping(format);

		DWORD d3dusage = 0;
		D3DPOOL pool = D3DPOOL_MANAGED;
		__MappingUsage(d3dusage, pool, usage);
		HRESULT hr = D3DXCreateTexture(d, (UINT)width, (UINT)height, (UINT)mip, d3dusage, d3dformat, pool, &_texture); 
		MK_PROCESS_COM_ERROR(hr);

		__ObtainActualTextureSize();

		_mip = mip;
		_format = format;
		_usage = usage;

		_imageInfo.Width = width;
		_imageInfo.Height = height;

		return TRUE;

Exit0:
		return FALSE;
	}

	void CD3D9Texture2D::__MappingUsage(DWORD& d3dusage, D3DPOOL& pool, TEXTURE_USAGE usage)
	{
		switch (usage)
		{
		case TU_DYNAMIC:
			{
				d3dusage = D3DUSAGE_DYNAMIC;
				pool = D3DPOOL_DEFAULT;
			}
			break;

		case TU_RENDER_TARGET:
			{
				d3dusage = D3DUSAGE_RENDERTARGET;
				pool = D3DPOOL_DEFAULT;
			}
			break;

		case TU_NORMAL:
			{
				d3dusage = 0;
				pool = D3DPOOL_MANAGED;
			}
			break;

		default:
			break;
		}
	}

	BOOL CD3D9Texture2D::__GetLockFlag(DWORD& flag)
	{
		switch (_usage)
		{
		case TU_DYNAMIC:
			flag = D3DLOCK_DISCARD;
			return TRUE;

		case TU_RENDER_TARGET:
			return FALSE; 

		case TU_NORMAL:
			flag = 0;
			return TRUE;

		default:
			return FALSE;
		}
	}   

	BOOL CD3D9Texture2D::LockRect(void** pData, DWORD* pitch, RECT* pRect)
	{
		if (!_texture)
			return FALSE;

		D3DLOCKED_RECT rect;
		DWORD flag = 0;
		if (!__GetLockFlag(flag))
			return FALSE;

		HRESULT hr = _texture->LockRect(0, &rect, pRect, flag);
		MK_PROCESS_COM_ERROR(hr);

		*pData = rect.pBits;
		*pitch = (DWORD)rect.Pitch;

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CD3D9Texture2D::Unlock()
	{
		if (!_texture)
			return FALSE;

		HRESULT hr = _texture->UnlockRect(0);
		MK_PROCESS_COM_ERROR(hr);

		return TRUE;

Exit0:
		return FALSE;
	}

	BOOL CD3D9Texture2D::GetRenderTargetSurface(LPDIRECT3DSURFACE9& surface)
	{
		if (_usage != TU_RENDER_TARGET)
			return FALSE;

		HRESULT result = _texture->GetSurfaceLevel(0, &surface);
		MK_PROCESS_COM_ERROR(result);

		return TRUE;

Exit0:
		return FALSE;
	}

	BOOL CD3D9Texture2D::SaveToFile(const char* fileName)
	{
		HRESULT hr = D3DXSaveTextureToFileA(fileName, D3DXIFF_DDS, _texture, NULL);
		MK_PROCESS_COM_ERROR(hr);

		return TRUE;
Exit0:
		return FALSE;
	}

	CD3D9Texture3D::CD3D9Texture3D() : _texture(NULL)
	{
		memset(&_imageInfo, 0, sizeof(_imageInfo));
	}

	CD3D9Texture3D::CD3D9Texture3D(const char* fileName) : CTexture3D(fileName), _texture(NULL)
	{
		memset(&_imageInfo, 0, sizeof(_imageInfo));
	}

	CD3D9Texture3D::~CD3D9Texture3D()
	{
		SAFE_RELEASE(_texture);
	}

	BOOL CD3D9Texture3D::Load()
	{
		BOOL ret = FALSE;
		CMemoryBlock block;

		ret = GetEngine()->GetFileSystem()->ReadFile(_fileName.c_str(), &block);
		MK_PROCESS_ERROR(ret);

		CD3D9RenderDevice* rd = (CD3D9RenderDevice*)GetEngine()->GetRenderDevice();
		LPDIRECT3DDEVICE9 d = rd->GetD3DDevice();

		HRESULT hr = D3DXCreateVolumeTextureFromFileInMemoryEx(d,
			(void*)block.GetBuffer(), (UINT)block.GetSize(),
			0, 0, 0, D3DX_FROM_FILE, TU_NORMAL, D3DFMT_FROM_FILE, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT, 0,
			&_imageInfo, NULL, &_texture);
		MK_PROCESS_COM_ERROR(hr);

		ret = TRUE;

Exit0:
		return ret;
	}
}